/*
 * $Id: SkvEnv.cc,v 1.3 2006-04-24 08:42:10 bacon Exp $
 */

#include <xpp/utl/SkvEnv.h>
#include <xpp/bas/String.h>
#include <xp/bas/assert.h>

#define SCTN_KEY_SPLITTER XP_CHAR('*')

namespace xpp
{
	namespace utl
	{

		using namespace xpp::bas;

		///////////////////////////
		// item (un)registration //
		///////////////////////////
		int SkvEnv::addItem (
			const xp_char_t* name, const xp_char_t* dval, ProbeProc probe)
		{
			xp_char_t*  sctn, * key;
			xp_size_t sctn_len, key_len;

			if (split_name (name, &sctn, &sctn_len, &key, &key_len) == -1) return -1;
		
			for (ItemList::Node* np = item_list.head(); np; np = np->forward()) {
				Item& item = np->value;
				if (xp_strxcmp(sctn, sctn_len, item.sctn) == 0 &&
				    xp_strxcmp(key, key_len, item.key)  == 0) return -1;
			}	
		
			Item& item = item_list.append (Item());
			xp_strxncpy (item.sctn, xp_countof(item.sctn), sctn, sctn_len);
			xp_strxncpy (item.key,  xp_countof(item.key),  key,  key_len);
			xp_char_t splitter[2] = { SCTN_KEY_SPLITTER, XP_CHAR('\0') };
			xp_strxapp (
				item.name, xp_countof(item.name), 
				item.sctn, splitter, item.key, XP_NULL);
			xp_strxcpy (item.dval, xp_countof(item.dval), dval);
			item.probe = probe;
		
			// set its default value
			if (setItemValue (name, item.dval) == XP_NULL) return -1;
			return 0;
		}
		
		int SkvEnv::removeItem (const xp_char_t* name)
		{
			xp_char_t*  sctn, * key;
			xp_size_t sctn_len, key_len;
			if (split_name (name, &sctn, &sctn_len, &key, &key_len) == -1) return -1;
		
			for (ItemList::Node* np = item_list.head(); np; np = np->forward()) {
				Item& item = np->value;
				if (xp_strxcmp(sctn, sctn_len, item.sctn) == 0 &&
				    xp_strxcmp(key, key_len, item.key)  == 0) {
					item_list.remove (np);
					return 0;
				}
			}	
		
			return -1;
		}
		
		xp_char_t* SkvEnv::itemName (const xp_char_t* sctn, const xp_char_t* key)
		{
			xp_char_t* name;
			if (getItemNameAndDval (sctn, key, &name, XP_NULL) == -1) return XP_NULL;
			return name;
		}
		
		xp_char_t* SkvEnv::itemDval (const xp_char_t* sctn, const xp_char_t* key)
		{
			xp_char_t* dval;
			if (getItemNameAndDval (sctn, key, XP_NULL, &dval) == -1) return XP_NULL;
			return dval;
		}
		
		int SkvEnv::getItemNameAndDval (
			const xp_char_t* sctn, const xp_char_t* key, 
			xp_char_t** name, xp_char_t** dval) 
		{
		
			xp_char_t* s, * k;
			xp_size_t sl, kl;
			String::tokenize (sctn, XP_NULL, &s, &sl);
			String::tokenize (key,  XP_NULL, &k, &kl);
		
			for (ItemList::Node* np = item_list.head(); np; np = np->forward()) {
				Item& item = np->value;
				if (xp_strxcmp(s, sl, item.sctn) == 0 &&
				    xp_strxcmp(k, kl, item.key)  == 0) {
					if (name) *name = item.name;
					if (dval) *dval = item.dval;
					return 0;
				}
			}
			return -1;
		}
		
		////////////////////////////
		// environment management //
		////////////////////////////
		const xp_char_t* SkvEnv::itemValueOf (const xp_char_t* name) const
		{
			xp_char_t*  sctn, * key;
			xp_size_t sctn_len, key_len;
			if (split_name (name, &sctn, &sctn_len, &key, &key_len) == -1) return XP_NULL;
		
			for (ItemList::Node* np = item_list.head(); np; np = np->forward()) {
				Item& item = np->value;
				if (xp_strxcmp(sctn, sctn_len, item.sctn) == 0 &&
				    xp_strxcmp(key,  key_len,  item.key)  == 0) {
					return valueOf (item.sctn, item.key);
				}
			}	

			return XP_NULL;
		}
		
		const xp_char_t* SkvEnv::setItemValue (
			const xp_char_t* name, const xp_char_t* val)
		{
			xp_char_t* sctn, * key;
			xp_size_t sctn_len, key_len;

			if (split_name(name, &sctn, &sctn_len, &key, &key_len) == -1) return XP_NULL;
		
			for (ItemList::Node* np = item_list.head(); np; np = np->forward()) {
				Item& item = np->value;
				if (xp_strxcmp(sctn, sctn_len, item.sctn) == 0 &&
				    xp_strxcmp(key, key_len, item.key)  == 0) {
					if ((this->*item.probe)(val) == -1) return XP_NULL;
					setValue (item.sctn, item.key, val);
					return val;
				}
			}	

			return XP_NULL;
		}
		
		///////////////////
		// miscellaneous //
		///////////////////
		int SkvEnv::split_name (const xp_char_t* name,
			xp_char_t** sctn, xp_size_t* sctn_len, xp_char_t** key, xp_size_t* key_len)
		{
			xp_assert (name     != XP_NULL);
			xp_assert (sctn     != XP_NULL);
			xp_assert (sctn_len != XP_NULL);
			xp_assert (key      != XP_NULL);
			xp_assert (key_len  != XP_NULL);
		
			xp_char_t* s, * k, * p;
			xp_size_t sl, kl;
		
			xp_char_t spr[] = { SCTN_KEY_SPLITTER, XP_CHAR('\0') };
			p = String::tokenize (name, spr, &s, &sl);
			if (p == XP_NULL || sl == 0) return -1;
			String::tokenize (p, XP_NULL, &k, &kl);
			if (kl == 0) return -1;
		
			*sctn     = s;
			*sctn_len = sl;
			*key      = k;
			*key_len  = kl;
		
			return 0;
		}
		
		xp_size_t SkvEnv::make_name (
			xp_char_t* buf, xp_size_t sz, const xp_char_t* sctn, const xp_char_t* key)
		{
			xp_char_t splitter[2] = { SCTN_KEY_SPLITTER, XP_CHAR('\0') };
			return xp_strxapp (buf, sz, sctn, splitter, key, XP_NULL);
		}

		/*
		void SkvEnv::print ()
		{
			SkvList::Node* np;
			SkvSctn::Node* np2;

			for (np = this->head(); np; np = np->forward()) {
				SkvSctn& s = np->value;
				printf (XP_TEXT("[%s]\n"), s.name());
 
				for (np2 = s.head(); np2; np2 = np2->forward()) {
					SkvPair& p = np2->value();
					printf (XP_TEXT("%s=%s\n"),
						p.key.buffer(), p.value.buffer());
				}
			}
		}
		*/

	}
}
